/*
 * 
 * Copyright 2010 David H Pollack
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 * 
 * 
 */

package com.dhpollack.rotogss.servlets;

import static com.google.appengine.api.labs.taskqueue.TaskOptions.Builder.*;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;

import javax.jdo.JDOObjectNotFoundException;
import javax.jdo.PersistenceManager;
import javax.jdo.Transaction;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;

import net.oauth.OAuthException;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.dhpollack.rotogss.data.AppConfig;
import com.dhpollack.rotogss.data.RotoGSSLeagueXml;
import com.dhpollack.rotogss.data.RotoGSSUsers;
import com.dhpollack.rotogss.data.YahooGames;
import com.dhpollack.rotogss.helper.AppHelper;
import com.dhpollack.rotogss.helper.PMF;
import com.dhpollack.rotogss.helper.XPATH;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.labs.taskqueue.Queue;
import com.google.appengine.api.labs.taskqueue.QueueFactory;
import com.simpleyql.Api;
import com.simpleyql.ApiFactory;
import com.simpleyql.QueryResult;


/**
 * @author davidpl
 *
 */
public class YahooGetLeaguesServlet extends HttpServlet {
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 8290907691849661982L;

	private static final AppConfig APP_CONFIG = AppHelper.get();

	private static XPath xPath = XPATH.get().newXPath();
	
	private static Queue queue = QueueFactory.getDefaultQueue();

	private RotoGSSUsers rotouser;
	
	private Key user_key;
	
	private String yahoo_authdata;
	
	protected void service(HttpServletRequest req, HttpServletResponse resp) 
    throws ServletException, IOException {

		PersistenceManager pm = PMF.get().getPersistenceManager();

        Map<String, String> leagues = new HashMap<String, String>();

		String user = req.getParameter("user");
		
		if(user == null) {
			log("No user set");
			return;
		}
		
		Integer game_key = (req.getParameter("game_key") != null) ? Integer.parseInt((String)req.getParameter("game_key")) : null;
		
		setUser(user);

		if(game_key != null) {
			Key key = new KeyFactory.Builder(YahooGames.class.getSimpleName(),game_key).getKey();
			YahooGames game = pm.getObjectById(YahooGames.class, key);
			if(game.getIsTracking()) {
				String url = buildYahooUrl(game.getGameId());
				String leagues_xml = getLeaguesXmlFromYahoo(url);
				// Yahoo XML gotten
				NodeList nodes = parseLeaguesXml(leagues_xml, "//leagues/league");
				int node_count = nodes.getLength();
				try {
					XPathExpression xpath_league_key = xPath.compile("league_key");
					XPathExpression xpath_league_update = xPath.compile("league_update_timestamp");
					for(int i=0;i<node_count;i++) {
						String league_xml = nodeToString(nodes.item(i));
						String league_key = (String) xpath_league_key.evaluate(nodes.item(i),XPathConstants.STRING);
						String league_update = (String) xpath_league_update.evaluate(nodes.item(i),XPathConstants.STRING);
						log("Processing league " + league_key + " with timestamp " + league_update);
						if(storeLeagueXmlDatastore(league_xml, league_key, league_update) >= (8*60*60*1000)) {
		                    
							queue.add(url(req.getServletPath()).param("user", user)
		                    		     .param("game_key", String.valueOf(game_key))
		                    		     .countdownMillis((long) (3.5*60*60*1000)));
							
						}
	                    leagues.put(league_key, league_update);
					}
				} catch (XPathExpressionException e) {
					e.printStackTrace();
				}
			} else {
				log("Currently we are not tracking this game.");
			}

		} else {
			log("No Game Keys Found");
		}
		req.setAttribute("leagues", leagues);
		req.getRequestDispatcher("/admin/yahooParseLeagues").include(req, resp);
	}

	private void setUser(String user) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		
		Key rotogss_userkey = new KeyFactory.Builder(RotoGSSUsers.class.getSimpleName(),user).getKey();
		try {
			RotoGSSUsers found_user = pm.getObjectById(RotoGSSUsers.class, rotogss_userkey);
			user_key = found_user.getGoogleUserKey();
			yahoo_authdata = found_user.getYahooOAuthData();
			rotouser = pm.detachCopy(found_user);
		} catch(JDOObjectNotFoundException e) {
			log("No user found for " + user);
			log("Key used: " + rotogss_userkey.toString());
			return;
		}

		
	}
	
	private NodeList parseLeaguesXml(String leagues_xml, String expression) {
		NodeList n = null;
		try {
			InputSource is = new InputSource(new StringReader(leagues_xml));
			Document response = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is);
			n = (NodeList)xPath.evaluate( expression , response, XPathConstants.NODESET);
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (XPathExpressionException e) {
			e.printStackTrace();
		}
		return n;
	}

	private String buildYahooUrl(Integer game_id) {
		
		StringBuilder s = new StringBuilder();
		s.append("http://fantasysports.yahooapis.com/fantasy/v2/users;use_login=1/games;game_keys=");
		s.append(game_id);
		s.append("/leagues;out=settings,standings");
		return s.toString();
	}

	private String getLeaguesXmlFromYahoo(String url)
	throws IOException {
		
		PersistenceManager pm = PMF.get().getPersistenceManager();
		
		Api api = ApiFactory.getApiInstance(
				APP_CONFIG.getYahooConsumerKey(),
				APP_CONFIG.getYahooConsumerSecret(),
				APP_CONFIG.getBaseUrl() + "/simpleyqlcallback/",
	            false, null);
		
		QueryResult qr = null;
		
		try {
			qr = api.query2(url, yahoo_authdata);
		} catch (OAuthException e) {
			log("Error posting to Y!:", e);
			return null;
		} catch (RuntimeException e) {
			if (e.getMessage().equals("Unable to complete the HTTP request")) {
				if (e.getCause().getMessage()
						.contains("Timeout while fetching")) {
					log("Unable to complete Yahoo! query", e);
				} else {
					log("Error posting to Y!:", e);
					return null;
				}
			} else {
				throw e;
			}
		}
		
		// Update token, if needed (for future requests)
	    if (qr != null && !qr.getAuthdata().equals(yahoo_authdata)) {
	    	rotouser.setYahooOAuthData(qr.getAuthdata());
	    	try {
	    		pm.makePersistent(rotouser);
	    	} finally {
	    		pm.close();
	    	}
	    }
	    
	    if(!pm.isClosed()) {
	    	pm.close();
	    }


		return qr.getText();
	}

	private String nodeToString(Node node) {
		StringWriter sw = new StringWriter();
		try {
		Transformer t = TransformerFactory.newInstance().newTransformer();
		t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
		t.transform(new DOMSource(node), new StreamResult(sw));
		} catch (TransformerException te) {
		System.out.println("nodeToString Transformer Exception");
		}
		return sw.toString();
	}

	private long storeLeagueXmlDatastore(String league_xml, String league_key,
			String league_update) {
		
		boolean success = false;
		
		Long date_diff = (long) -1;
		
		Long date = Long.parseLong(league_update) * 1000;
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Transaction tx = pm.currentTransaction();
		Key key = new KeyFactory.Builder(RotoGSSLeagueXml.class.getSimpleName(), league_key).getKey();
		RotoGSSLeagueXml lxml = null;
		try {
			tx.begin();
			try {
				lxml = pm.getObjectById(RotoGSSLeagueXml.class, key);
				log("Found League Xml");
				if(!date.equals(lxml.getUpdateTimestamp().getTime())) {
					log("Updating xml due to timestamp");
					log("Old TS: " + lxml.getUpdateTimestamp().getTime()
						+ "\nNew TS: " + date);
					lxml.setLeagueXml(league_xml);
					lxml.setUpdateTimestamp(league_update);
					date_diff = (long) 0;
				} else {
					 date_diff = date - lxml.getUpdateTimestamp().getTime();
				}
				if(!lxml.getGoogleUserKeys().contains(user_key)) {
					log("Adding user to league xml");
					lxml.addGoogleUserKey(user_key);
				}
			} catch(JDOObjectNotFoundException e) {
				log("League not found and being created");
				lxml = new RotoGSSLeagueXml();
				lxml.setLeagueKey(key);
				lxml.setLeagueXml(league_xml);
				lxml.setUpdateTimestamp(league_update);
				lxml.setGoogleUserKeys(new LinkedHashSet<Key>());
				lxml.addGoogleUserKey(user_key);
				success = true;
			}
			pm.makePersistent(lxml);
			tx.commit();
		} finally {
			if(tx.isActive()) {
				tx.rollback();
			}
		}
		
		return date_diff;
		// TODO Put the XML into the Memcache.
		
	}
	

}
